package com.simpleactors;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author Dmitry
 */
public abstract class Actor {
    private Thread thread;
    private final LinkedBlockingQueue<MessageWrapper> queue;
    private ActorSystem actorSystem;
    private String name;

    public Actor(String name) {
        this.queue = new LinkedBlockingQueue();
        this.name = name;
    }

    public Actor(String name, int max) {
        this.queue = new LinkedBlockingQueue(max);
        this.name = name;
    }

    void setActorSystem(ActorSystem actorSystem) {
        this.actorSystem = actorSystem;
    }

    public String getName() {
        return name;
    }

    public int getQueueSize() {
        return queue.size();
    }

    void setName(String name) {
        this.name = name;
    }

    protected Actor getActor(String actorName) {
        Actor actor = actorSystem.getActor(actorName);
        if (actor == null) {
            actor = actorSystem.nullActor;
        }
        return actor;
    }

    public void start() {
        thread = new Thread() {
            @Override
            public void run() {
                while (!interrupted()) {
                    try {
                        MessageWrapper value = queue.poll(2, TimeUnit.SECONDS);
                        if (value == null) {
                            continue;
                        }

                        receive(value.getValue(), value.getSender());
                    } catch (InterruptedException ex) {
                        break;
                    }
                }
            }
        };
        thread.setDaemon(true);
        thread.start();
    }

    public boolean send(Object value) {
        return send(value, actorSystem.nullActor);
    }

    public boolean send(Object value, Actor sender) {
        MessageWrapper wrapper = new MessageWrapper(value, sender);
        try {
            boolean result = queue.offer(wrapper, 5, TimeUnit.HOURS);
            return result;
        } catch (InterruptedException ex) {
            return false;
        }
    }

    public void stop() {
        thread.interrupt();
    }

    @Override
    public String toString() {
        return "Actor " + name;
    }

    public abstract void receive(Object value, Actor receiver);
}
